package controllers

import (
	"encoding/json"
	"fmt"
	"gitee.com/yxd1990/golang-common/debug"
	"strconv"
	"strings"

	"gitee.com/yxd1990/golang-common/constants"
	errors2 "gitee.com/yxd1990/golang-common/errors"
	"gitee.com/yxd1990/golang-common/utils"
	"github.com/astaxie/beego"
	"github.com/astaxie/beego/logs"
	"github.com/astaxie/beego/orm"
)

type BaseController struct {
	beego.Controller
}

const authKey string = "Authorization"

var (
	ErrNotAuthed  = errors2.EmitCode(constants.NotAuthed)
	ErrNotLogined = errors2.EmitCode(constants.NotLogin)
)

func (this *BaseController) SuccessEmpty() {
	this.ResponseJson(constants.NoError, "", nil)
}

func (this *BaseController) Success(data ...interface{}) {
	if len(data) == 0 {
		this.SuccessEmpty()
		return
	}
	if len(data) > 1 {
		if err, ok := data[1].(error); ok {
			this.CheckError(err)
		}
	}
	this.ResponseJson(constants.NoError, "", data[0])
}

func (this *BaseController) Fail(msg string, args ...interface{}) {
	errCode := constants.ErrMsg
	if len(args) > 0 {
		if code, ok := args[0].(constants.ErrorCode); ok {
			errCode = code
		}
	}
	this.ResponseJson(errCode, msg, nil)
}

func (c *BaseController) Options() {
	c.Data["json"] = map[string]interface{}{"status": 200, "message": "ok", "moreinfo": ""}
	c.ServeJSON()
}

func (this *BaseController) CheckError(err error) {
	if err != nil {
		if e, ok := err.(*errors2.E); ok {
			this.Fail(e.Error(), e.Code())
			return
		} else {
			if this.IsNoRows(err) {
				this.Fail("data not founded", constants.NotFounded)
				return
			}
			logs.Error("%s,stack:%s", err.Error(), debug.GetStackInfo())
		}
		this.Fail("系统异常")
	}
}
func (this *BaseController) LogError(err error) {
	if err != nil {
		logs.Error(err)
	}
}

func (this *BaseController) ResponseJson(errcode constants.ErrorCode, msg string, data interface{}) {
	ret := map[string]interface{}{"code": errcode, "msg": msg, "data": data}
	this.Data["json"] = ret
	this.Ctx.ResponseWriter.Status = 200
	this.ServeJSON()
	this.StopRun()
	return
}

func (this *BaseController) ResponseRawJson(data interface{}) {
	this.Data["json"] = data
	this.ServeJSON()
	this.StopRun()
	return
}

// 抛出异常
func (this *BaseController) Throw(args ...interface{}) {
	logs.Error("controller error:", args...)
	this.Fail("controller error")
}

func (this *BaseController) SetUserInfo(loginUser *utils.LoginUser) (err error) {
	var authToken string
	authToken, err = utils.LoginUtil.GenerateToken(loginUser)
	if err != nil {
		return
	}
	this.Ctx.ResponseWriter.Header().Add(authKey, authToken)
	return
}

// 刷新jwt 用户信息
func (this *BaseController) RefreshUserInfo(loginUser *utils.LoginUser) {
	if utils.LoginUtil.NeedRefreshToken(loginUser) {
		_ = this.SetUserInfo(loginUser)
	}
}

func (this *BaseController) GetUserInfo(params ...bool) *utils.LoginUser {
	result, err := this.GetUserInfoWithErr(params...)
	if err == nil {
		return result
	}
	if e, ok := err.(*errors2.E); ok {
		this.Fail("", e.Code())
	} else {
		this.CheckError(err)
	}
	return nil
}
func (this *BaseController) GetUserIdWithErr(params ...bool) (userId int, err error) {
	var loginUser *utils.LoginUser
	loginUser, err = this.GetUserInfoWithErr(params...)
	if err == nil {
		userId = loginUser.Id
	}
	return
}

func (this *BaseController) GetUserInfoWithErr(params ...bool) (loginUser *utils.LoginUser, err error) {
	checkStatus := true
	if len(params) > 0 {
		checkStatus = params[0]
	}
	authToken := this.Ctx.Request.Header.Get(authKey)
	if authToken != "" {
		loginUser, err = utils.LoginUtil.GetLoginUser(authToken)
		if err == nil {
			if checkStatus {
				if loginUser.Status >= constants.YesStatus { // 用户有效
					this.RefreshUserInfo(loginUser)
					return
				}
			} else {
				this.RefreshUserInfo(loginUser)
				return
			}
			// 代表没有授权，需要授权
			err = ErrNotAuthed
			return
		} else {
			logs.Error("getUserInfo error:", err)
		}
	}
	// token失效或者不存在 返回403
	err = ErrNotLogined
	return
}

func (this *BaseController) GetUserId(params ...bool) int {
	return this.GetUserInfo(params...).Id
}

func (this *BaseController) GetUserExtraId(params ...bool) interface{} {
	return this.GetUserInfo(params...).Extra
}

func (this *BaseController) GetUserExtraIdAsInt(params ...bool) int {
	rt, _ := utils.ParseInt(this.GetUserExtraId(params...))
	return rt
}

func (this *BaseController) GetUserExtraIdAsString(params ...bool) string {
	rt := utils.ParseString(this.GetUserExtraId(params...))
	return rt
}

func (this *BaseController) GetUserExtraIdAsFloat(params ...bool) float64 {
	rt, _ := utils.ParseFloat64(this.GetUserExtraId(params...))
	return rt
}

// 查询为空
func (this *BaseController) IsNoRows(err error) bool {
	return err == orm.ErrNoRows
}

// 获取ip地址
func (this *BaseController) GetIpAddress() string {
	ip := this.Ctx.Request.Header.Get("X-Real-IP")
	if ip == "" {
		ip = this.Ctx.Input.IP()
	}
	return ip
}

func (this *BaseController) AssetNotEmpty(value interface{}, errmsg string) {
	switch v := value.(type) {
	case string:
		if v == "" || strings.TrimSpace(v) == "" {
			this.Fail(errmsg)
		}
		break
	}
}

func (this *BaseController) ParseInputJSON(data interface{}) {
	err := json.Unmarshal(this.Ctx.Input.RequestBody, data)
	this.CheckError(err)
}

func (this *BaseController) GetMapFromInputJson() (data map[string]interface{}) {
	if inputData, ok := this.Data["inputJson"]; ok {
		data = inputData.(map[string]interface{})
	} else {
		data = make(map[string]interface{})
		if len(this.Ctx.Input.RequestBody) > 0 {
			err := json.Unmarshal(this.Ctx.Input.RequestBody, &data)
			this.CheckError(err)
		}
		this.Data["inputJson"] = data
	}
	return
}

func (this *BaseController) GetIntFromInputJson(field string, defaultValue ...int) int {
	data := this.GetMapFromInputJson()
	dv := 0
	if len(defaultValue) > 0 {
		dv = defaultValue[0]
	}
	if v, ok := data[field]; ok {
		return utils.ParseIntOrDefault(v, dv)
	}
	return dv
}

func (this *BaseController) GetStringFromInputJson(field string, defaultValue ...string) string {
	data := this.GetMapFromInputJson()
	dv := ""
	if len(defaultValue) > 0 {
		dv = defaultValue[0]
	}
	if v, ok := data[field]; ok {
		return fmt.Sprintf("%v", v)
	}
	return dv
}

func (this *BaseController) GetFloat64FromInputJson(field string, defaultValue ...float64) float64 {
	data := this.GetMapFromInputJson()
	dv := 0.0
	if len(defaultValue) > 0 {
		dv = defaultValue[0]
	}
	if v, ok := data[field]; ok {
		v, err := utils.ParseFloat64(v)
		if err == nil {
			dv = v
		}
	}
	return dv
}

func (this *BaseController) GetBoolFromInputJson(field string, defaultValue ...bool) (bool, error) {
	data := this.GetMapFromInputJson()
	dv := false
	if len(defaultValue) > 0 {
		dv = defaultValue[0]
	}
	if v, ok := data[field]; ok {
		return strconv.ParseBool(fmt.Sprintf("%v", v))
	}
	return dv, nil
}

func (this *BaseController) isJsonRequest() bool {
	if this.Ctx.Request.Method == "POST" && strings.Contains(this.Ctx.Input.Header("content-type"), "application/json") {
		return true
	}
	return false
}
func (this *BaseController) GetInt(key string, def ...int) (int, error) {
	if this.isJsonRequest() {
		return this.GetIntFromInputJson(key, def...), nil
	}
	return this.Controller.GetInt(key, def...)
}

func (this *BaseController) GetFloat(key string, def ...float64) (float64, error) {
	if this.isJsonRequest() {
		return this.GetFloat64FromInputJson(key, def...), nil
	}
	return this.Controller.GetFloat(key, def...)
}
func (this *BaseController) GetString(key string, def ...string) string {
	if this.isJsonRequest() {
		return this.GetStringFromInputJson(key, def...)
	}
	return this.Controller.GetString(key, def...)
}

func (this *BaseController) GetBool(key string, def ...bool) (bool, error) {
	if this.isJsonRequest() {
		return this.GetBoolFromInputJson(key, def...)
	}
	return this.Controller.GetBool(key, def...)
}
